﻿using System;
using System.IO;
using System.Text;
using System.Xml.Linq;
using UtilZ.Dotnet.Ex.Audio;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.Normalization;

namespace ConAppCore.Test
{
#pragma warning disable 8500

    unsafe internal class TestWaveHelper
    {
        public static void Test()
        {
            //TestGetWaveHead();

            //TestFillWavHeadPcm();
            //TestFillWavHeadIeeeFloat();
            //TestFillWavHeadIeeeFloat2();
            //TestFillWavHeadADPCM();



            //TestAToP();
            //TestUToP();
            //TestPToA();
            //TestPToU();
            //TestAToU();
            //TestUToA();
            //TestPcm32ToP();
            //TestPcmToPcm32();

            //TestAdpcmDataToPcmData();
            TestAdpcmToP();
            //TestPToAdpcm();
            //TestPToCSVD();
            //TestCSVDToP();

            //string outFile4 = @"D:\Temp\FM\naudio_remple4.wav";
            //using var resultStream4 = new FileStream(outFile4, FileMode.Open, FileAccess.ReadWrite);
            //using var ptr4 = new UnmanagedMemoryPointer<float>((resultStream4.Length - WaveHelper.WAVE_HEAD_SIZE) / sizeof(float));
            //resultStream4.Position = WaveHelper.WAVE_HEAD_SIZE;
            //resultStream4.Read(ptr4.GetSpanZ().Cast<byte>().ToSpan());
            //Span<float> audioData4 = ptr4.GetSpan();

        }

        private static void TestFillWavHeadADPCM()
        {
            string outFile = @"D:\HBData\我爱你中国伴奏_ms_adpcm_test.wav";
            //WaveHelper.FillWavHead(outFile, WaveEncoding.Adpcm, 16, 16000, 2, 0);

            var waveInfo = WaveHelper.GetWavHead("D:\\HBData\\x_adpcm.wav");
            outFile = @"D:\HBData\x_pcm.wav";
            WaveHelper.FillWavHead(outFile, WaveEncoding.Pcm, 16, waveInfo.SampleRate, waveInfo.ChannelCount, 0);

            //outFile = @"D:\HBData\x_pcm.wav";
            //WaveHelper.FillWavHead(outFile, WaveEncoding.Pcm, 16, 16000, 2, 0);
        }

        private static void TestFillWavHeadIeeeFloat2()
        {
            string filePath = @"D:\Temp\FM\sdr_src_audio.wav";
            var wavwInfo = WaveHelper.GetWavHead(filePath);
            var headSize = WaveHelper.GetWaveHeadSize(wavwInfo.Encoding, true);
            //WaveHelper.FillWavHead(filePath , WaveEncoding.IeeeFloat, 32, 32000, 2, 0, false);
            using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            using var dataPtr = new UnmanagedMemoryPointer<float>((fs.Length - headSize) / sizeof(float));
            SpanZ<float> originData = dataPtr.GetSpanZ();

            fs.Position = headSize;
            fs.Read(originData.Cast<byte>().ToSpan());

            using var retDataPtr1 = new UnmanagedMemoryPointer<float>(originData.Length);
            var normalizer1 = new Normalizer<float, float>(new NormalizerOptions(-1f, 1f) { Pre = 0.000000001d, SlidFrames = 1000 });
            normalizer1.Excute(originData, retDataPtr1.GetSpanZ());
            string outFilePath1 = @"D:\Temp\FM\sdr_src_audio1.wav";
            using var fs1 = new FileStream(outFilePath1, FileMode.Create, FileAccess.Write);
            WaveHelper.FillWavHead(fs1, WaveEncoding.IeeeFloat, DataSampleBits.BIT32, wavwInfo.SampleRate, wavwInfo.ChannelCount, (uint)(retDataPtr1.Length * sizeof(float)), false);
            fs1.Write(retDataPtr1.Cast<byte>().ToSpan());

            using var retDataPtr2 = new UnmanagedMemoryPointer<short>(originData.Length);
            var normalizer2 = new Normalizer<float, short>(new NormalizerOptions(-32767, 32767) { Pre = 0.000000001d, SlidFrames = 1000 });
            normalizer2.Excute(originData, retDataPtr2.GetSpanZ());
            string outFilePath2 = @"D:\Temp\FM\sdr_src_audio2.wav";
            using var fs2 = new FileStream(outFilePath2, FileMode.Create, FileAccess.Write);
            WaveHelper.FillWavHead(fs2, WaveEncoding.Pcm, DataSampleBits.BIT16, wavwInfo.SampleRate, wavwInfo.ChannelCount, (uint)(retDataPtr1.Length * sizeof(short)), false);
            fs2.Write(retDataPtr2.Cast<byte>().ToSpan());
        }

        private static void TestFillWavHeadIeeeFloat()
        {
            string outFile = @"D:\Temp\FM\naudio_remple5.wav";
            //WaveHelper.FillWavHead32(outFile, 16000, 1);

            using (var resultStream = new FileStream(outFile, FileMode.Open, FileAccess.ReadWrite))
            {
                //WaveHelper.FillWavHead32(resultStream, 16000, 1, (uint)(resultStream.Length - WaveHelper.WAVE_HEAD_SIZE32));
                WaveHelper.FillWavHead(resultStream, WaveEncoding.IeeeFloat, 32, 16000, 1);
            }
        }

        private static void TestFillWavHeadPcm()
        {
            string filePath = @"D:\Temp\FM\xx.wav";
            //WaveHelper.FillWavHead(filePath, 16, 256000, 2, 0);
            //WaveHelper.FillWavHead(filePath, 32, 256000 / 2, 2, 0);
            //var waveInfo = WaveHelper.GetWavHead(filePath);


            //filePath = @"D:\Temp\FM\stereoPre.wav";
            //filePath = @"D:\Temp\FM\stereoAft.wav";
            filePath = @"D:\Temp\FM\PlayerFiller.wav";
            //WaveHelper.FillWavHead(filePath, 32, 256000 / 4, 1, 0);
            //WaveHelper.FillWavHead(filePath, 16, 256000 / 2, 1, 0);

            filePath = @"D:\Temp\FM\音频数据_NN.wav";
            //WaveHelper.FillWavHead(filePath, 16, 256000, 1, -1);
            //WaveHelper.FillWavHead(filePath, WaveEncoding.Pcm, 16, 256000, 1, 0);

            filePath = @"D:\pcm.wav";
            WaveHelper.FillWavHead(filePath, WaveEncoding.Pcm, 16, 16000, 1, 0);
        }



        private static void TestPcmToPcm32()
        {
            string srcFilePath = @"D:\HBData\bay_bay_bay_R.wav";
            string destFilePath = @"D:\HBData\z_bay_bay_bay_p2p32.wav";
            FileEx.TryDeleFile(destFilePath);
            WaveHelper.PcmToIeeeFloat(srcFilePath, destFilePath);
        }

        private static void TestPcm32ToP()
        {
            string srcFilePath = @"D:\HBData\bay_bay_bay_32ex.wav";
            srcFilePath = @"D:\HBData\bay_bay_bay_32.wav";
            string destFilePath = @"D:\HBData\z_bay_bay_bay_32toP.wav";
            FileEx.TryDeleFile(destFilePath);
            WaveHelper.IeeeFloatToPcm(srcFilePath, destFilePath);
        }

        private static void TestPToCSVD()
        {
            string srcFilePath = @"D:\HBData\我爱你中国伴奏_ms_adpcm_test.wav";
            string destFilePath = @"D:\HBData\x_我爱你中国伴奏_adpcm2p.wav";
            FileEx.TryDeleFile(destFilePath);
            WaveHelper.PcmToCSVD(srcFilePath, destFilePath);
        }

        private static void TestCSVDToP()
        {
            string srcFilePath = @"D:\HBData\我爱你中国伴奏_ms_adpcm_test.wav";
            string destFilePath = @"D:\HBData\x_我爱你中国伴奏_adpcm2p.wav";
            FileEx.TryDeleFile(destFilePath);
            WaveHelper.CsvdToPcm(srcFilePath, destFilePath);
        }


        private static void TestAdpcmDataToPcmData()
        {
            string srcFilePath = @"D:\HBData\我爱你中国伴奏_ms_adpcm_test.wav";
            srcFilePath = @"D:\HBData\x_adpcm.wav";
            string destFilePath = @"D:\HBData\x_我爱你中国伴奏_adpcm2p_222.wav";
            FileEx.TryDeleFile(destFilePath);

            using (var disposabler = new Disposabler())
            {
                var srcFileStream = new FileStream(srcFilePath, FileMode.Open, FileAccess.Read);
                disposabler.Add(srcFileStream, 0);

                int srcFileWaveHeadSize = WaveHelper.GetWaveHeadSize(WaveEncoding.Adpcm);
                var srcDataPtr = new UnmanagedMemoryPointer<byte>(srcFileStream.Length - srcFileWaveHeadSize);
                disposabler.Add(srcDataPtr, 0);

                var waveInfo = WaveHelper.GetWavHead(srcFileStream);
                srcFileStream.Position = srcFileWaveHeadSize;
                srcFileStream.Read(srcDataPtr.GetSpan());


                var destDataPtr = new UnmanagedMemoryPointer<short>(srcDataPtr.Length * 2);
                disposabler.Add(destDataPtr, 0);

                bool g726_16 = true;
                var converter = new G726();
                var adpcmData = new Span<sbyte>((sbyte*)srcDataPtr.DataPtr, srcDataPtr.Length);
                var pcmData = new Span<short>(destDataPtr.DataPtr, destDataPtr.Length);

                converter.g726_decode(adpcmData, pcmData, g726_16);

                //const int adpcmStep = 240, pcmStep = 480;
                //int adpcmOffset = 0, pcmOffset = 0;
                //while (true)
                //{
                //    if (adpcmData.Length - adpcmOffset < adpcmStep)
                //    {
                //        break;
                //    }

                //    converter.g726_decode(adpcmData.Slice(adpcmOffset, adpcmStep), pcmData.Slice(pcmOffset, pcmStep), g726_16);
                //    adpcmOffset += adpcmStep;
                //    pcmOffset += pcmStep;
                //}
                //if (pcmOffset < pcmData.Length)
                //{
                //    pcmData = pcmData.Slice(0, pcmOffset);
                //}

                DirectoryInfoEx.CheckFilePathDirectory(destFilePath);
                var destFileStream = new FileStream(destFilePath, FileMode.CreateNew, FileAccess.Write);
                disposabler.Add(destFileStream, 0);

                destFileStream.Position = WaveHelper.GetWaveHeadSize(WaveEncoding.Pcm);
                destFileStream.Write(destDataPtr.Cast<byte>().ToSpan());

                WaveHelper.FillWavHead(destFileStream, WaveEncoding.Pcm, 16, waveInfo.SampleRate, waveInfo.ChannelCount, (uint)(pcmData.Length * 2));
            }

        }

        private static void TestAdpcmToP()
        {
            string srcFilePath = @"D:\HBData\我爱你中国伴奏_ms_adpcm_test.wav";
            //srcFilePath = @"D:\HBData\x_adpcm.wav";
            string destFilePath = @"D:\HBData\x_我爱你中国伴奏_adpcm2p.wav";
            FileEx.TryDeleFile(destFilePath);
            WaveHelper.AdpcmToPcm(srcFilePath, destFilePath);
        }

        private static void TestPToAdpcm()
        {
            string srcFilePath = @"D:\HBData\z_我爱你中国伴奏_a2p.wav";
            string destFilePath = @"D:\HBData\x_我爱你中国伴奏_p2adpcm.wav";
            FileEx.TryDeleFile(destFilePath);
            WaveHelper.PcmToAdpcm(srcFilePath, destFilePath);
        }


        private static void TestAToP()
        {
            //string ufilePath = @"D:\HBData\a_我爱你中国伴奏.wav";
            //var waveInfo = WaveHelper.GetWavHead(ufilePath);

            //using var srcFS = new FileStream(ufilePath, FileMode.Open, FileAccess.Read);
            //using var srcDataPtr = new UnmanagedMemoryPointer<byte>(srcFS.Length - WaveHelper.WAVE_HEAD_SIZE58);
            //srcFS.Position = WaveHelper.WAVE_HEAD_SIZE58;
            //srcFS.Read(srcDataPtr.GetSpan());

            ////a->pcm
            //string filePath2 = @"D:\HBData\我爱你中国伴奏_a2p.wav";
            //using var destFS = new FileStream(filePath2, FileMode.CreateNew, FileAccess.Write);
            //destFS.Position = WaveHelper.WAVE_HEAD_SIZE;

            ////var writer = new BinaryWriter(destFS);
            ////for (int i = 0; i < srcDataPtr.Length; i++)
            ////{
            ////    writer.Write(AUConverter.ALawToLinear(srcDataPtr[i]));
            ////}

            //using var pcmDataPtr = new UnmanagedMemoryPointer<short>(srcDataPtr.Length);
            //WaveHelper.ALawToPcm(srcDataPtr.GetSpanZ(), pcmDataPtr.GetSpanZ());
            //destFS.Write(pcmDataPtr.Cast<byte>().ToSpan());

            //WaveHelper.FillWavHead(destFS, WaveFormatEncoding.Pcm, 16, waveInfo.SampleRate, (short)waveInfo.ChannelCount, 0);

            string srcFilePath = @"D:\HBData\a_我爱你中国伴奏.wav";
            string destFilePath = @"D:\HBData\z_我爱你中国伴奏_a2p.wav";
            WaveHelper.ALawToPcm(srcFilePath, destFilePath);
        }

        private static void TestUToP()
        {
            //string ufilePath = @"D:\HBData\u_阳光总在风雨后.wav";
            //var waveInfo = WaveHelper.GetWavHead(ufilePath);

            //using var srcFS = new FileStream(ufilePath, FileMode.Open, FileAccess.Read);
            //using var srcDataPtr = new UnmanagedMemoryPointer<byte>(srcFS.Length - WaveHelper.WAVE_HEAD_SIZE58);
            //srcFS.Position = WaveHelper.WAVE_HEAD_SIZE58;
            //srcFS.Read(srcDataPtr.GetSpan());

            ////a->pcm
            //string filePath2 = @"D:\HBData\阳光总在风雨后_u2p.wav";
            //using var destFS = new FileStream(filePath2, FileMode.CreateNew, FileAccess.Write);
            //destFS.Position = WaveHelper.WAVE_HEAD_SIZE;

            ////var writer = new BinaryWriter(destFS);
            ////for (int i = 0; i < srcDataPtr.Length; i++)
            ////{
            ////    writer.Write(AUConverter.ULawToLinear(srcDataPtr[i]));
            ////}

            //using var pcmDataPtr = new UnmanagedMemoryPointer<short>(srcDataPtr.Length);
            //WaveHelper.MuLawToPcm(srcDataPtr.GetSpanZ(), pcmDataPtr.GetSpanZ());
            //destFS.Write(pcmDataPtr.Cast<byte>().ToSpan());

            //WaveHelper.FillWavHead(destFS, WaveFormatEncoding.Pcm, 16, waveInfo.SampleRate, (short)waveInfo.ChannelCount, 0);

            string srcFilePath = @"D:\HBData\u_阳光总在风雨后.wav";
            string destFilePath = @"D:\HBData\z_阳光总在风雨后_u2p.wav";
            WaveHelper.MuLawToPcm(srcFilePath, destFilePath);
        }

        private static void TestPToA()
        {
            //string filePath = @"D:\HBData\bay_bay_bay_R.wav";
            //var waveInfo = WaveHelper.GetWavHead(filePath);

            //using var srcFS = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            //using var srcDataPtr = new UnmanagedMemoryPointer<short>((srcFS.Length - WaveHelper.WAVE_HEAD_SIZE) / 2);
            //srcFS.Position = WaveHelper.WAVE_HEAD_SIZE58;
            //srcFS.Read(srcDataPtr.Cast<byte>().ToSpan());

            ////a->pcm
            //string filePath2 = @"D:\HBData\bay_bay_bay_p2a.wav";
            //using var destFS = new FileStream(filePath2, FileMode.CreateNew, FileAccess.Write);
            //destFS.Position = WaveHelper.WAVE_HEAD_SIZE;

            ////var writer = new BinaryWriter(destFS);
            ////for (int i = 0; i < srcDataPtr.Length; i++)
            ////{
            ////    writer.Write(AUConverter.LinearToALaw(srcDataPtr[i]));
            ////}

            //using var aLAwDataPtr = new UnmanagedMemoryPointer<byte>(srcDataPtr.Length);
            //WaveHelper.PcmToALaw(srcDataPtr.GetSpanZ(), aLAwDataPtr.GetSpanZ());
            //destFS.Write(aLAwDataPtr.GetSpan());

            //WaveHelper.FillWavHead(destFS, WaveFormatEncoding.ALaw, 8, waveInfo.SampleRate, (short)waveInfo.ChannelCount, 0);


            string srcFilePath = @"D:\HBData\bay_bay_bay_R.wav";
            string destFilePath = @"D:\HBData\z_我爱你中国伴奏_p2a.wav";
            WaveHelper.PcmToALaw(srcFilePath, destFilePath);
        }

        private static void TestPToU()
        {
            //string filePath = @"D:\HBData\bay_bay_bay_R.wav";
            //var waveInfo = WaveHelper.GetWavHead(filePath);

            //using var srcFS = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            //using var srcDataPtr = new UnmanagedMemoryPointer<short>((srcFS.Length - WaveHelper.WAVE_HEAD_SIZE) / 2);
            //srcFS.Position = WaveHelper.WAVE_HEAD_SIZE58;
            //srcFS.Read(srcDataPtr.Cast<byte>().ToSpan());

            ////a->pcm
            //string filePath2 = @"D:\HBData\bay_bay_bay_p2u.wav";
            //using var destFS = new FileStream(filePath2, FileMode.CreateNew, FileAccess.Write);
            //destFS.Position = WaveHelper.WAVE_HEAD_SIZE;

            ////var writer = new BinaryWriter(destFS);
            ////for (int i = 0; i < srcDataPtr.Length; i++)
            ////{
            ////    writer.Write(AUConverter.LinearToULaw(srcDataPtr[i]));
            ////}

            //using var muLawDataPtr = new UnmanagedMemoryPointer<byte>(srcDataPtr.Length);
            //WaveHelper.PcmToMuLaw(srcDataPtr.GetSpanZ(), muLawDataPtr.GetSpanZ());
            //destFS.Write(muLawDataPtr.GetSpan());

            //WaveHelper.FillWavHead(destFS, WaveFormatEncoding.MuLaw, 8, waveInfo.SampleRate, (short)waveInfo.ChannelCount, 0);


            string srcFilePath = @"D:\HBData\bay_bay_bay_R.wav";
            string destFilePath = @"D:\HBData\z_我爱你中国伴奏_p2u.wav";
            WaveHelper.PcmToMuLaw(srcFilePath, destFilePath);
        }


        private static void TestAToU()
        {
            string srcFilePath = @"D:\HBData\a_我爱你中国伴奏.wav";
            string destFilePath = @"D:\HBData\z_我爱你中国伴奏_a2u.wav";
            WaveHelper.ALawToMuLaw(srcFilePath, destFilePath);
        }

        private static void TestUToA()
        {
            string srcFilePath = @"D:\HBData\u_阳光总在风雨后.wav";
            string destFilePath = @"D:\HBData\z_阳光总在风雨后_u2a.wav";
            WaveHelper.MuLawToALaw(srcFilePath, destFilePath);
        }








        private static void TestGetWaveHead()
        {
            string filePath = @"D:\HBData\a_我爱你中国伴奏.wav";
            WaveInfo waveInfo1 = WaveHelper.GetWavHead(filePath);

            filePath = @"D:\HBData\u_阳光总在风雨后.wav";
            WaveInfo waveInfo2 = WaveHelper.GetWavHead(filePath);

            filePath = @"D:\HBData\bay_bay_bay_R.wav";
            WaveInfo waveInfo3 = WaveHelper.GetWavHead(filePath);

            filePath = @"D:\HBData\p_成都.wav";
            WaveInfo waveInfo4 = WaveHelper.GetWavHead(filePath);

            filePath = @"D:\HBData\我爱你中国伴奏_ms_adpcm.wav";
            //filePath = @"D:\HBData\x_adpcm.wav";
            WaveInfo waveInfo5 = WaveHelper.GetWavHead(filePath);

            filePath = @"D:\HBData\bay_bay_bay_32ex.wav";
            filePath = @"D:\HBData\bay_bay_bay_32.wav";
            WaveInfo waveInfo6 = WaveHelper.GetWavHead(filePath);


        }


    }

    unsafe internal class G726
    {
        public G726()
        {

        }


        private const int AUDIO_ENCODING_LINEAR = 3;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bitstream">240</param>
        /// <param name="pcm">480</param>
        public void g726_decode(Span<sbyte> bitstream, Span<short> pcm, bool g726_16)
        {
            g726_state state_ptr = new g726_state();

            g726_init_state(&state_ptr);

            int i, len, inData;
            if (g726_16)
            {
                len = bitstream.Length / 2;//120
                for (i = 0; i < len; i++)
                {
                    inData = (bitstream[i] & 192) >> 6;
                    pcm[i * 4] = g726_16_decoder(inData, AUDIO_ENCODING_LINEAR, &state_ptr);

                    inData = (bitstream[i] & 48) >> 4;
                    pcm[i * 4 + 1] = g726_16_decoder(inData, AUDIO_ENCODING_LINEAR, &state_ptr);

                    inData = (bitstream[i] & 12) >> 2;
                    pcm[i * 4 + 2] = g726_16_decoder(inData, AUDIO_ENCODING_LINEAR, &state_ptr);

                    inData = bitstream[i] & 3;
                    pcm[i * 4 + 3] = g726_16_decoder(inData, AUDIO_ENCODING_LINEAR, &state_ptr);
                }
            }
            else
            {
                len = bitstream.Length;//240
                for (i = 0; i < len; i++)
                {
                    inData = (bitstream[i] & 240) >> 4;
                    pcm[i * 2] = g726_32_decoder(inData, AUDIO_ENCODING_LINEAR, &state_ptr);

                    inData = bitstream[i] & 15;
                    pcm[i * 2 + 1] = g726_32_decoder(inData, AUDIO_ENCODING_LINEAR, &state_ptr);
                }
            }
        }





        static int[] qtab_721_32 = new int[7] { -124, 80, 178, 246, 300, 349, 400 };
        /*
         * Maps G.721 code word to reconstructed scale factor normalized log
         * magnitude values.
         */
        static short[] _dqlntab_32 = new short[16]  {-2048, 4, 135, 213, 273, 323, 373, 425,
                425, 373, 323, 273, 213, 135, 4, -2048};

        /* Maps G.721 code word to log of scale factor multiplier. */
        static short[] _witab_32 = new short[16]  {-12, 18, 41, 64, 112, 198, 355, 1122,
                1122, 355, 198, 112, 64, 41, 18, -12};
        /*
         * Maps G.721 code words to a set of values whose long and short
         * term averages are computed and then compared to give an indication
         * how stationary (steady state) the signal is.
         */
        static short[] _fitab_32 = new short[16]  {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,
                0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0};
        /*
 * g721_decoder()
 *
 * Description:
 *
 * Decodes a 4-bit code of G.721 encoded data of i and
 * returns the resulting linear PCM, A-law or u-law value.
 * return -1 for unknown out_coding value.
 */
        short g726_32_decoder(int inData, int out_coding, g726_state* state_ptr)
        {
            int sezi;
            int sez;            /* ACCUM */
            int sei;
            int se;
            int y;              /* MIX */
            int dq;
            int sr;             /* ADDB */
            int dqsez;
            long lino;

            inData &= 0x0f;              /* mask to get proper bits */
            sezi = predictor_zero(state_ptr);
            sez = sezi >> 1;
            sei = sezi + predictor_pole(state_ptr);
            se = sei >> 1;          /* se = estimated signal */
            y = step_size(state_ptr);   /* dynamic quantizer step size */
            dq = reconstruct(inData & 0x08, _dqlntab_32[inData], y); /* quantized diff. */
            sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq; /* reconst. signal */
            dqsez = sr - se + sez;          /* pole prediction diff. */

            update(4, y, _witab_32[inData] << 5, _fitab_32[inData], dq, sr, dqsez, state_ptr);

            switch (out_coding)
            {
                //case AUDIO_ENCODING_ALAW:
                //    return (tandem_adjust_alaw(sr, se, y, i, 8, qtab_721_32));
                //case AUDIO_ENCODING_ULAW:
                //    return (tandem_adjust_ulaw(sr, se, y, i, 8, qtab_721_32));
                case AUDIO_ENCODING_LINEAR:
                    lino = (long)sr << 2;  /* this seems to overflow a short*/
                    lino = lino > 32767 ? 32767 : lino;
                    lino = lino < -32768 ? -32768 : lino;
                    return (short)lino;//(sr << 2);	/* sr was 14-bit dynamic range */
                default:
                    return (-1);
            }
        }











        /*
 * Maps G.723_16 code word to reconstructed scale factor normalized log
 * magnitude values.  Comes from Table 11/G.726
 */
        static short[] _dqlntab_16 = new short[4] { 116, 365, 365, 116 };

        /* Maps G.723_16 code word to log of scale factor multiplier.
 *
 * _witab[4] is actually {-22 , 439, 439, -22}, but FILTD wants it
 * as WI << 5  (multiplied by 32), so we'll do that here 
 */
        static short[] _witab_16 = new short[4] { -704, 14048, 14048, -704 };

        /*
         * Maps G.723_16 code words to a set of values whose long and short
         * term averages are computed and then compared to give an indication
         * how stationary (steady state) the signal is.
         */

        /* Comes from FUNCTF */
        static short[] _fitab_16 = new short[4] { 0, 0xE00, 0xE00, 0 };

        /*
         * g723_16_decoder()
         *
         * Decodes a 2-bit CCITT G.723_16 ADPCM code and returns
         * the resulting 16-bit linear PCM, A-law or u-law sample value.
         * -1 is returned if the output coding is unknown.
         */
        short g726_16_decoder(int i, int out_coding, g726_state* state_ptr)
        {
            int sezi;
            int sez;            /* ACCUM */
            int sei;
            int se;
            int y;              /* MIX */
            int dq;
            int sr;             /* ADDB */
            int dqsez;

            i &= 0x03;          /* mask to get proper bits */
            sezi = predictor_zero(state_ptr);
            sez = sezi >> 1;
            sei = sezi + predictor_pole(state_ptr);
            se = sei >> 1;          /* se = estimated signal */

            y = step_size(state_ptr);   /* adaptive quantizer step size */
            dq = reconstruct(i & 0x02, _dqlntab_16[i], y); /* unquantize pred diff */

            sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */

            dqsez = sr - se + sez;          /* pole prediction diff. */

            update(2, y, _witab_16[i], _fitab_16[i], dq, sr, dqsez, state_ptr);

            switch (out_coding)
            {
                //case AUDIO_ENCODING_ALAW:
                //    return (tandem_adjust_alaw(sr, se, y, i, 2, qtab_723_16));
                //case AUDIO_ENCODING_ULAW:
                //    return (tandem_adjust_ulaw(sr, se, y, i, 2, qtab_723_16));
                case AUDIO_ENCODING_LINEAR:
                    return (short)(sr << 2);   /* sr was of 14-bit dynamic range */
                default:
                    return (-1);
            }
        }

        /*
 * update()
 *
 * updates the state variables for each output code
 */
        void
        update(
            int code_size,  /* distinguish 723_40 with others */
            int y,      /* quantizer step size */
            int wi,     /* scale factor multiplier */
            int fi,     /* for long/short term energies */
            int dq,     /* quantized prediction difference */
            int sr,     /* reconstructed signal */
            int dqsez,      /* difference from 2-pole predictor */
            g726_state* state_ptr)  /* coder state pointer */
        {
            int cnt;
            int mag, exp;   /* Adaptive predictor, FLOAT A */
            int a2p;        /* LIMC */
            int a1ul;       /* UPA1 */
            int pks1;       /* UPA2 */
            int fa1;
            int tr;         /* tone/transition detector */
            int ylint, thr2, dqthr;
            int ylfrac, thr1;
            int pk0;

            pk0 = (dqsez < 0) ? 1 : 0;  /* needed in updating predictor poles */

            mag = dq & 0x7FFF;      /* prediction difference magnitude */
            /* TRANS */
            ylint = (int)(state_ptr->yl >> 15);    /* exponent part of yl */
            ylfrac = (int)((state_ptr->yl >> 10) & 0x1F);  /* fractional part of yl */
            thr1 = (32 + ylfrac) << ylint;      /* threshold */
            thr2 = (ylint > 9) ? 31 << 10 : thr1;   /* limit thr2 to 31 << 10 */
            dqthr = (thr2 + (thr2 >> 1)) >> 1;  /* dqthr = 0.75 * thr2 */
            if (state_ptr->td == 0)     /* signal supposed voice */
                tr = 0;
            else if (mag <= dqthr)      /* supposed data, but small mag */
                tr = 0;         /* treated as voice */
            else                /* signal is data (modem) */
                tr = 1;

            /*
             * Quantizer scale factor adaptation.
             */

            /* FUNCTW & FILTD & DELAY */
            /* update non-steady state step size multiplier */
            state_ptr->yu = y + ((wi - y) >> 5);

            /* LIMB */
            if (state_ptr->yu < 544)    /* 544 <= yu <= 5120 */
                state_ptr->yu = 544;
            else if (state_ptr->yu > 5120)
                state_ptr->yu = 5120;

            /* FILTE & DELAY */
            /* update steady state step size multiplier */
            state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6);

            /*
             * Adaptive predictor coefficients.
             */
            if (tr == 1)
            {           /* reset a's and b's for modem signal */
                state_ptr->a[0] = 0;
                state_ptr->a[1] = 0;
                state_ptr->b[0] = 0;
                state_ptr->b[1] = 0;
                state_ptr->b[2] = 0;
                state_ptr->b[3] = 0;
                state_ptr->b[4] = 0;
                state_ptr->b[5] = 0;
                a2p = 0;
            }
            else
            {           /* update a's and b's */
                pks1 = pk0 ^ state_ptr->pk[0];      /* UPA2 */

                /* update predictor pole a[1] */
                a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7);
                if (dqsez != 0)
                {
                    fa1 = (pks1 != 0) ? state_ptr->a[0] : -state_ptr->a[0];
                    if (fa1 < -8191)    /* a2p = function of fa1 */
                        a2p -= 0x100;
                    else if (fa1 > 8191)
                        a2p += 0xFF;
                    else
                        a2p += fa1 >> 5;

                    if ((pk0 ^ state_ptr->pk[1]) != 0)
                        /* LIMC */
                        if (a2p <= -12160)
                            a2p = -12288;
                        else if (a2p >= 12416)
                            a2p = 12288;
                        else
                            a2p -= 0x80;
                    else if (a2p <= -12416)
                        a2p = -12288;
                    else if (a2p >= 12160)
                        a2p = 12288;
                    else
                        a2p += 0x80;
                }

                /* TRIGB & DELAY */
                state_ptr->a[1] = a2p;

                /* UPA1 */
                /* update predictor pole a[0] */
                state_ptr->a[0] -= state_ptr->a[0] >> 8;
                if (dqsez != 0)
                {
                    if (pks1 == 0)
                        state_ptr->a[0] += 192;
                    else
                        state_ptr->a[0] -= 192;
                }
                /* LIMD */
                a1ul = 15360 - a2p;
                if (state_ptr->a[0] < -a1ul)
                    state_ptr->a[0] = -a1ul;
                else if (state_ptr->a[0] > a1ul)
                    state_ptr->a[0] = a1ul;

                /* UPB : update predictor zeros b[6] */
                for (cnt = 0; cnt < 6; cnt++)
                {
                    if (code_size == 5)     /* for 40Kbps G.723 */
                        state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9;
                    else            /* for G.721 and 24Kbps G.723 */
                        state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8;
                    if ((dq & 0x7FFF) != 0)
                    {           /* XOR */
                        if ((dq ^ state_ptr->dq[cnt]) >= 0)
                            state_ptr->b[cnt] += 128;
                        else
                            state_ptr->b[cnt] -= 128;
                    }
                }
            }

            for (cnt = 5; cnt > 0; cnt--)
                state_ptr->dq[cnt] = state_ptr->dq[cnt - 1];
            /* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */
            if (mag == 0)
            {
                state_ptr->dq[0] = (short)((dq >= 0) ? 0x20 : 0xFC20);
            }
            else
            {
                exp = quan(mag, power2, 15);
                state_ptr->dq[0] = (short)((dq >= 0) ?
                    (exp << 6) + ((mag << 6) >> exp) :
                    (exp << 6) + ((mag << 6) >> exp) - 0x400);
            }

            state_ptr->sr[1] = state_ptr->sr[0];
            /* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */
            if (sr == 0)
            {
                state_ptr->sr[0] = 0x20;
            }
            else if (sr > 0)
            {
                exp = quan(sr, power2, 15);
                state_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp);
            }
            else if (sr > -32768)
            {
                mag = -sr;
                exp = quan(mag, power2, 15);
                state_ptr->sr[0] = (exp << 6) + ((mag << 6) >> exp) - 0x400;
            }
            else
                state_ptr->sr[0] = 0xFC20;

            /* DELAY A */
            state_ptr->pk[1] = state_ptr->pk[0];
            state_ptr->pk[0] = pk0;

            /* TONE */
            if (tr == 1)        /* this sample has been treated as data */
                state_ptr->td = 0;  /* next one will be treated as voice */
            else if (a2p < -11776)  /* small sample-to-sample correlation */
                state_ptr->td = 1;  /* signal may be data */
            else                /* signal is voice */
                state_ptr->td = 0;

            /*
             * Adaptation speed control.
             */
            state_ptr->dms += (fi - state_ptr->dms) >> 5;       /* FILTA */
            state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7);  /* FILTB */

            if (tr == 1)
                state_ptr->ap = 256;
            else if (y < 1536)                  /* SUBTC */
                state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
            else if (state_ptr->td == 1)
                state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
            else if (ABS((state_ptr->dms << 2) - state_ptr->dml) >=
                (state_ptr->dml >> 3))
                state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
            else
                state_ptr->ap += (-state_ptr->ap) >> 4;
        }

        private static int ABS(int a)
        {
            return (((a) < 0) ? -(a) : a);
        }

        /*
 * reconstruct()
 *
 * Returns reconstructed difference signal 'dq' obtained from
 * codeword 'i' and quantization step size scale factor 'y'.
 * Multiplication is performed in log base 2 domain as addition.
 */
        int reconstruct(
            int sign,   /* 0 for non-negative value */
            int dqln,   /* G.72x codeword */
            int y)      /* Step size multiplier */
        {
            int dql;    /* Log of 'dq' magnitude */
            int dex;    /* Integer part of log */
            int dqt;
            int dq;     /* Reconstructed difference signal sample */

            dql = dqln + (y >> 2);  /* ADDA */

            if (dql < 0)
            {
                return ((sign != 0) ? -0x8000 : 0);
            }
            else
            {       /* ANTILOG */
                dex = (dql >> 7) & 15;
                dqt = 128 + (dql & 127);
                dq = (short)((dqt << 7) >> (14 - dex));
                return ((sign != 0) ? (dq - 0x8000) : dq);
            }
        }


        /*
 * step_size()
 *
 * computes the quantization step size of the adaptive quantizer.
 *
 */
        int step_size(g726_state* state_ptr)
        {
            int y;
            int dif;
            int al;

            if (state_ptr->ap >= 256)
                return (state_ptr->yu);
            else
            {
                y = (int)(state_ptr->yl >> 6);
                dif = state_ptr->yu - y;
                al = state_ptr->ap >> 2;
                if (dif > 0)
                    y += (dif * al) >> 6;
                else if (dif < 0)
                    y += (dif * al + 0x3F) >> 6;
                return (y);
            }
        }

        /*
 * predictor_pole()
 *
 * computes the estimated signal from 2-pole predictor.
 *
 */
        int
        predictor_pole(g726_state* state_ptr)
        {
            return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) +
                fmult(state_ptr->a[0] >> 2, state_ptr->sr[0]));
        }

        /*
 * predictor_zero()
 *
 * computes the estimated signal from 6-zero predictor.
 *
 */
        int predictor_zero(g726_state* state_ptr)
        {
            int i;
            int sezi;

            sezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]);
            for (i = 1; i < 6; i++)         /* ACCUM */
                sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]);
            return (sezi);
        }


        private static int[] power2 = new int[15] { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
            0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000 };
        /*
         * fmult()
         *
         * returns the integer product of the 14-bit integer "an" and
         * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn".
         */
        int fmult(int an, int srn)
        {
            int anmag;
            int anexp;
            int anmant;
            int wanexp;
            int wanmant;
            int retval;

            anmag = (an > 0) ? an : ((-an) & 0x1FFF);
            anexp = quan(anmag, power2, 15) - 6;
            anmant = (anmag == 0) ? 32 :
                (anexp >= 0) ? anmag >> anexp : anmag << -anexp;
            wanexp = anexp + ((srn >> 6) & 0xF) - 13;

            wanmant = (anmant * (srn & 077) + 0x30) >> 4;
            retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) :
                (wanmant >> -wanexp);

            return (((an ^ srn) < 0) ? -retval : retval);
        }


        /*
 * quan()
 *
 * quantizes the input val against the table of size short integers.
 * It returns i if table[i - 1] <= val < table[i].
 *
 * Using linear search for simple coding.
 */
        static int quan(int val, int[] table, int size)
        {
            int i;
            for (i = 0; i < size; i++)
                //if (val < *table++)
                if (val < table[i])
                    break;
            return (i);
        }

        void g726_init_state(g726_state* state_ptr)
        {
            int cnta;

            state_ptr->yl = 34816;
            state_ptr->yu = 544;
            state_ptr->dms = 0;
            state_ptr->dml = 0;
            state_ptr->ap = 0;
            for (cnta = 0; cnta < 2; cnta++)
            {
                state_ptr->a[cnta] = 0;
                state_ptr->pk[cnta] = 0;
                state_ptr->sr[cnta] = 32;
            }
            for (cnta = 0; cnta < 6; cnta++)
            {
                state_ptr->b[cnta] = 0;
                state_ptr->dq[cnta] = 32;
            }
            state_ptr->td = 0;
        }



        struct g726_state
        {
            public g726_state()
            {

            }

            public long yl;    /* Locked or steady state step size multiplier. */
            public int yu;     /* Unlocked or non-steady state step size multiplier. */
            public int dms;    /* Short term energy estimate. */
            public int dml;    /* Long term energy estimate. */
            public int ap;     /* Linear weighting coefficient of 'yl' and 'yu'. */

            public int[] a = new int[2];   /* Coefficients of pole portion of prediction filter. */
            public int[] b = new int[6];   /* Coefficients of zero portion of prediction filter. */

            public int[] pk = new int[2];
            /* Signs of previous two samples of a partially
				 * reconstructed signal. */

            public short[] dq = new short[6];
            /* int here fails in newupdate on encode!
				 * Previous 6 samples of the quantized difference
				 * signal represented in an internal floating point
				 * format.
				 */

            public int[] sr = new int[2];
            /* Previous 2 samples of the quantized difference
				 * signal represented in an internal floating point
				 * format. */

            public int td;     /* delayed tone detect, new in 1988 version */
        }
    }

}
